#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AI营销方案生成系统 - 基础数据模型
提供MongoDB文档模型的基础类和数据访问对象基类

主要功能：
- MongoDB文档模型基类
- 数据验证和序列化
- 数据访问对象(DAO)基类
- 通用CRUD操作

设计原则：
- 单一职责：每个模型只负责一个业务实体
- 数据完整性：通过验证确保数据一致性
- 可扩展性：支持模型继承和扩展
- 性能优化：合理使用索引和查询优化

Author: AI Marketing System
Date: 2024
"""

from abc import ABC, abstractmethod
from datetime import datetime, timezone
from typing import Dict, Any, List, Optional, Union
from bson import ObjectId
from pymongo.collection import Collection
from pymongo.database import Database
from pymongo import ASCENDING, DESCENDING
import json


class BaseModel(ABC):
    """
    MongoDB文档模型基类
    
    提供基础的文档操作方法：
    - 数据验证
    - 序列化/反序列化
    - 字段类型转换
    - 时间戳管理
    """
    
    # 集合名称，子类必须定义
    collection_name: str = None
    
    # 必需字段，子类可以重写
    required_fields: List[str] = []
    
    # 可选字段，子类可以重写
    optional_fields: List[str] = []
    
    # 默认字段值，子类可以重写
    default_values: Dict[str, Any] = {}
    
    def __init__(self, **kwargs):
        """
        初始化模型实例
        
        Args:
            **kwargs: 字段值
        """
        # 设置默认值
        for field, default_value in self.default_values.items():
            setattr(self, field, default_value)
        
        # 设置传入的字段值
        for field, value in kwargs.items():
            setattr(self, field, value)
        
        # 设置时间戳
        now = datetime.now(timezone.utc)
        if not hasattr(self, '_id'):
            self._id = None
        if not hasattr(self, 'created_at'):
            self.created_at = now
        if not hasattr(self, 'updated_at'):
            self.updated_at = now
    
    def validate(self) -> Dict[str, Any]:
        """
        验证模型数据
        
        Returns:
            Dict[str, Any]: 验证结果
                - valid: bool, 是否有效
                - errors: List[str], 错误列表
        """
        errors = []
        
        # 检查必需字段
        for field in self.required_fields:
            if not hasattr(self, field) or getattr(self, field) is None:
                errors.append(f"缺少必需字段: {field}")
        
        # 调用子类的自定义验证
        custom_errors = self._custom_validate()
        if custom_errors:
            errors.extend(custom_errors)
        
        return {
            'valid': len(errors) == 0,
            'errors': errors
        }
    
    def _custom_validate(self) -> List[str]:
        """
        自定义验证逻辑，子类可以重写
        
        Returns:
            List[str]: 错误列表
        """
        return []
    
    def to_dict(self, include_id: bool = True) -> Dict[str, Any]:
        """
        将模型转换为字典
        
        Args:
            include_id: 是否包含_id字段
            
        Returns:
            Dict[str, Any]: 字典表示
        """
        data = {}
        
        # 获取所有字段
        all_fields = self.required_fields + self.optional_fields + ['created_at', 'updated_at']
        if include_id:
            all_fields.append('_id')
        
        for field in all_fields:
            if hasattr(self, field):
                value = getattr(self, field)
                
                # 处理特殊类型
                if isinstance(value, ObjectId):
                    data[field] = str(value)
                elif isinstance(value, datetime):
                    data[field] = value.isoformat()
                else:
                    data[field] = value
        
        return data
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'BaseModel':
        """
        从字典创建模型实例
        
        Args:
            data: 字典数据
            
        Returns:
            BaseModel: 模型实例
        """
        # 处理特殊字段
        processed_data = data.copy()
        
        # 处理ObjectId
        if '_id' in processed_data and isinstance(processed_data['_id'], str):
            try:
                processed_data['_id'] = ObjectId(processed_data['_id'])
            except Exception:
                pass
        
        # 处理时间字段
        for time_field in ['created_at', 'updated_at']:
            if time_field in processed_data and isinstance(processed_data[time_field], str):
                try:
                    processed_data[time_field] = datetime.fromisoformat(processed_data[time_field].replace('Z', '+00:00'))
                except Exception:
                    pass
        
        return cls(**processed_data)
    
    def to_json(self, include_id: bool = True) -> str:
        """
        将模型转换为JSON字符串
        
        Args:
            include_id: 是否包含_id字段
            
        Returns:
            str: JSON字符串
        """
        return json.dumps(self.to_dict(include_id), ensure_ascii=False, indent=2)
    
    @classmethod
    def from_json(cls, json_str: str) -> 'BaseModel':
        """
        从JSON字符串创建模型实例
        
        Args:
            json_str: JSON字符串
            
        Returns:
            BaseModel: 模型实例
        """
        data = json.loads(json_str)
        return cls.from_dict(data)
    
    def update_timestamp(self):
        """
        更新时间戳
        """
        self.updated_at = datetime.now(timezone.utc)
    
    @classmethod
    @abstractmethod
    def create_indexes(cls, db: Database):
        """
        创建索引，子类必须实现
        
        Args:
            db: 数据库实例
        """
        pass
    
    def __str__(self) -> str:
        """
        字符串表示
        """
        return f"{self.__class__.__name__}({self.to_dict(include_id=False)})"
    
    def __repr__(self) -> str:
        """
        调试表示
        """
        return self.__str__()


class BaseDAO(ABC):
    """
    数据访问对象基类
    
    提供基础的CRUD操作：
    - 创建文档
    - 查询文档
    - 更新文档
    - 删除文档
    - 分页查询
    """
    
    def __init__(self, db: Database = None):
        """
        初始化DAO
        
        Args:
            db: 数据库实例
        """
        self.db = db
        if not self.db:
            from core import get_db
            self.db = get_db()
        
        if not self.db:
            raise RuntimeError("数据库连接未初始化")
    
    @property
    @abstractmethod
    def collection_name(self) -> str:
        """
        集合名称，子类必须实现
        """
        pass
    
    @property
    @abstractmethod
    def model_class(self):
        """
        模型类，子类必须实现
        """
        pass
    
    @property
    def collection(self) -> Collection:
        """
        获取集合对象
        
        Returns:
            Collection: 集合对象
        """
        return self.db[self.collection_name]
    
    def create(self, model: BaseModel) -> str:
        """
        创建文档
        
        Args:
            model: 模型实例
            
        Returns:
            str: 文档ID
            
        Raises:
            ValueError: 数据验证失败
        """
        # 验证数据
        validation = model.validate()
        if not validation['valid']:
            raise ValueError(f"数据验证失败: {', '.join(validation['errors'])}")
        
        # 更新时间戳
        model.update_timestamp()
        
        # 插入文档
        data = model.to_dict(include_id=False)
        result = self.collection.insert_one(data)
        
        return str(result.inserted_id)
    
    def find_by_id(self, doc_id: Union[str, ObjectId]) -> Optional[BaseModel]:
        """
        根据ID查找文档
        
        Args:
            doc_id: 文档ID
            
        Returns:
            Optional[BaseModel]: 模型实例或None
        """
        if isinstance(doc_id, str):
            try:
                doc_id = ObjectId(doc_id)
            except Exception:
                return None
        
        doc = self.collection.find_one({'_id': doc_id})
        if doc:
            return self.model_class.from_dict(doc)
        return None
    
    def find_one(self, filter_dict: Dict[str, Any]) -> Optional[BaseModel]:
        """
        查找单个文档
        
        Args:
            filter_dict: 查询条件
            
        Returns:
            Optional[BaseModel]: 模型实例或None
        """
        doc = self.collection.find_one(filter_dict)
        if doc:
            return self.model_class.from_dict(doc)
        return None
    
    def find_many(
        self,
        filter_dict: Dict[str, Any] = None,
        sort: List[tuple] = None,
        limit: int = None,
        skip: int = None
    ) -> List[BaseModel]:
        """
        查找多个文档
        
        Args:
            filter_dict: 查询条件
            sort: 排序条件
            limit: 限制数量
            skip: 跳过数量
            
        Returns:
            List[BaseModel]: 模型实例列表
        """
        if filter_dict is None:
            filter_dict = {}
        
        cursor = self.collection.find(filter_dict)
        
        if sort:
            cursor = cursor.sort(sort)
        if skip:
            cursor = cursor.skip(skip)
        if limit:
            cursor = cursor.limit(limit)
        
        return [self.model_class.from_dict(doc) for doc in cursor]
    
    def update_by_id(
        self,
        doc_id: Union[str, ObjectId],
        update_data: Dict[str, Any]
    ) -> bool:
        """
        根据ID更新文档
        
        Args:
            doc_id: 文档ID
            update_data: 更新数据
            
        Returns:
            bool: 是否更新成功
        """
        if isinstance(doc_id, str):
            try:
                doc_id = ObjectId(doc_id)
            except Exception:
                return False
        
        # 添加更新时间戳
        update_data['updated_at'] = datetime.now(timezone.utc)
        
        result = self.collection.update_one(
            {'_id': doc_id},
            {'$set': update_data}
        )
        
        return result.modified_count > 0
    
    def delete_by_id(self, doc_id: Union[str, ObjectId]) -> bool:
        """
        根据ID删除文档
        
        Args:
            doc_id: 文档ID
            
        Returns:
            bool: 是否删除成功
        """
        if isinstance(doc_id, str):
            try:
                doc_id = ObjectId(doc_id)
            except Exception:
                return False
        
        result = self.collection.delete_one({'_id': doc_id})
        return result.deleted_count > 0
    
    def count(self, filter_dict: Dict[str, Any] = None) -> int:
        """
        统计文档数量
        
        Args:
            filter_dict: 查询条件
            
        Returns:
            int: 文档数量
        """
        if filter_dict is None:
            filter_dict = {}
        
        return self.collection.count_documents(filter_dict)
    
    def paginate(
        self,
        page: int = 1,
        per_page: int = 20,
        filter_dict: Dict[str, Any] = None,
        sort: List[tuple] = None
    ) -> Dict[str, Any]:
        """
        分页查询
        
        Args:
            page: 页码（从1开始）
            per_page: 每页数量
            filter_dict: 查询条件
            sort: 排序条件
            
        Returns:
            Dict[str, Any]: 分页结果
        """
        if filter_dict is None:
            filter_dict = {}
        
        # 计算跳过数量
        skip = (page - 1) * per_page
        
        # 查询数据
        items = self.find_many(
            filter_dict=filter_dict,
            sort=sort,
            limit=per_page,
            skip=skip
        )
        
        # 统计总数
        total = self.count(filter_dict)
        
        # 计算分页信息
        total_pages = (total + per_page - 1) // per_page
        has_prev = page > 1
        has_next = page < total_pages
        
        return {
            'items': [item.to_dict() for item in items],
            'pagination': {
                'page': page,
                'per_page': per_page,
                'total': total,
                'total_pages': total_pages,
                'has_prev': has_prev,
                'has_next': has_next,
                'prev_page': page - 1 if has_prev else None,
                'next_page': page + 1 if has_next else None
            }
        }